/*
 * @Author: SongXiao
 * @Date: 2023-02-27 15:39:51
 * @LastEditors: SongXiao
 * @LastEditTime: 2023-04-20 20:11:07
 * @Description: file content
 */
import React, { useEffect } from 'react';
import './index.scss';
import jsonData from './data.json';
import water from './image/water.svg';
import dayjs from 'dayjs';
import rain from './weather-img/rain-avg.png';
import rainNoSplash from './weather-img/rain-nosplash-avg.png';
import sleet from './weather-img/sleet-avg.png';
import sleetNoSplash from './weather-img/sleet-nosplash-avg.png';
import snow from './weather-img/snow-avg.png';

import * as d3 from 'd3';

const WeatherChart = function () {

    // 对应星期
    const weekMap = {
        0: '周一',
        1: '周二',
        2: '周三',
        3: '周四',
        4: '周五',
        5: '周六',
        6: '周日'
    }

    // 对应内容
    const animateMap = [{
        id: 'patternRainAreaAnim',
        href: rain,
        codeArr: [300, 301, 302, 303, 306, 307, 308, 310, 311, 312, 315, 316, 317, 318, 399, 350, 351]
    }, {
        id: 'patternRainNoSplashAreaAnim',
        href: rainNoSplash,
        codeArr: [305, 309, 314]
    }, {
        id: 'patternSleetAreaAnim',
        href: sleet,
        codeArr: [304, 404, 405, 406, 456]
    }, {
        id: 'patternSleetNoSplashAreaAnim',
        href: sleetNoSplash,
        codeArr: [313]
    }, {
        id: 'patternSnowAreaAnim',
        href: snow,
        codeArr: [400, 401, 402, 403, 407, 408, 409, 410, 499, 457]
    }]

    /**
    * @description: 根据天气code得到动效id
    * @param {code} 天气code
    * @return: id 动效id
    */
    const findIdByCode = function (code) {
        for (let item of animateMap) {
            if (item.codeArr.includes(+code)) {
                return item.id
            }
        }
        return false
    }

    /**
    * @description: 获取天气图片
    * @param {str} 天气图片的代码
    * @return: obj 图片
    */
    const getIcon = function (key) {
        try {
            return require("./image/weather/" + key + ".png")
        }
        catch (err) {
            return require("./image/weather/999.png")
        }
    }

    useEffect(() => {
        d3.select('svg').selectAll('*').remove();

        let content = d3.select(".weather-chart-show")

        // 配置项
        // 边距
        let margin = {
            top: 40,
            right: 20,
            bottom: 50,
            left: 20
        };

        // 横坐标展示项
        let showAxisNum = 12 // 展示12个小时, 超出隐藏

        let contentWidth = content.style('width').substring(0, content.style('width').length - 2)
        let contentHeight = content.style('height').substring(0, content.style('height').length - 2)

        let svgWidth = jsonData.data.length * (contentWidth / showAxisNum)
        let svgHeight = contentHeight
        let svg = d3.select("body").select("svg")
            .attr('width', svgWidth)
            .attr('height', contentHeight)
        // .attr("transform", "translate(" + margin.left + "," + (margin.top) + ")");

        let chartContainer = svg.append("g").attr('class', 'chart-container')
        // .attr("width", svgWidth).attr("height", height)

        // 最高气温
        var maxDataTmp = 0;
        // 最低气温
        var minDataTmp = 0;
        // 找到第二天的分界线相关数据
        var nextDayObj = {}
        var data = []
        let nowCode = ""
        jsonData.data.forEach((d, i) => {
            // 用于绘制当前内容
            if (d.time.split(" ")[1] === "00:00") {
                nextDayObj = {
                    index: i,
                    timeValue: new Date(d.time).valueOf(),
                    showText: dayjs(d.time).format("MM-DD") + " " + weekMap[dayjs(d.time).format("d")]
                }
            }
            if (i === 0) {
                d.timeText = '现在'
            } else {
                d.timeText = d.time.split(" ")[1]
            }

            if (nowCode == d.cond_code) {
                d.showIcon = false
            } else {
                nowCode = d.cond_code
                d.showIcon = true
            }

            d.timeValue = new Date(d.time).valueOf();
            d.tmp = +d.tmp;
            if ((d.tmp + 10) > maxDataTmp) {
                // 最高点大于10度，使内容不占满
                maxDataTmp = d.tmp + 10;
            }
            if ((d.tmp - 10) < minDataTmp) {
                // 最低点小于5度，使内容不占满
                minDataTmp = d.tmp - 10
            }
            data.push({ ...d })
        });


        // 坐标x
        var x = d3.scaleTime()
            .range([0, svgWidth])
            .domain([data[0].timeValue, data[data.length - 1].timeValue]);

        // 坐标y
        var y = d3.scaleLinear()
            .range([svgHeight - margin.bottom, 0])
            .domain([minDataTmp, maxDataTmp])

        // 两个坐标距离
        let distance = x(data[1].timeValue) - x(data[0].timeValue)

        // 添加横坐标轴
        chartContainer.append("g")
            .attr("class", "x-axis")
            .selectAll(".x-axis")
            .data(data)
            .enter()
            .append("text")
            .attr("transform", `translate(${margin.left}, -${margin.bottom - 20})`)
            .attr("x", function (d, i) {
                return x(d.timeValue);
            })
            .attr("y", svgHeight)
            .text(function (d) {
                return d.timeText;
            });

        // 添加横坐标线
        let xLineG = chartContainer.append("g").attr("class", "x-line")

        xLineG.append("line")
            .attr('x1', 0)
            .attr('y1', svgHeight - margin.bottom)
            .attr('x2', svgWidth)
            .attr('y2', svgHeight - margin.bottom)
            .attr("stroke", "rgba(255, 255, 255, 0.2)") // 直线的颜色

        xLineG.append("line")
            .attr('x1', 0)
            .attr('y1', margin.top)
            .attr('x2', svgWidth)
            .attr('y2', margin.top)
            .attr("stroke", "rgba(255, 255, 255, 0.2)") // 直线的颜色

        // 添加辅助信息线
        let dashLineG = chartContainer.append("g").attr("class", "dash-line")
        // 前后日期隔断虚线
        dashLineG.append("line")
            .attr('x1', x(nextDayObj.timeValue) + margin.left + 20)
            .attr('y1', 0)
            .attr('x2', x(nextDayObj.timeValue) + margin.left + 20)
            .attr('y2', svgHeight - margin.bottom)
            .attr("stroke", "rgba(255, 255, 255, 0.2)") // 直线的颜色
            .attr("stroke-dasharray", "3 2")

        // 日期显示内容 - 今天
        dashLineG.append("text")
            .attr("x", x(nextDayObj.timeValue))
            .attr("y", margin.top)
            .text("今天")
            .attr('fill', '#fff')
            .attr('style', 'font-size: 12px;')

        // 日期显示内容 - 第二天    
        dashLineG.append("text")
            .attr("x", x(nextDayObj.timeValue) + margin.left + 35)
            .attr("y", margin.top)
            .text(nextDayObj.showText)
            .attr('fill', '#fff')
            .attr('style', 'font-size: 12px;')

        // 开始天气效果动效果
        // 定义动效果
        for (let item of animateMap) {
            let animateDefs = chartContainer.append("defs")
            let pattern = animateDefs.append("pattern")
                .attr("id", item.id)
                .attr("width", 3104)
                .attr("height", 207)
                .attr("patternUnits", "userSpaceOnUse")

            pattern.append("image").attr("href", item.href)
                .attr("width", 3104)
                .attr("height", 207)

            // 创建 animate 元素
            pattern.append("animate")
                .attr("attributeType", "XML")
                .attr("attributeName", "x")
                .attr("calcMode", "discrete")
                .attr("dur", "1s")
                .attr("repeatCount", "indefinite")
                .attr("values", "0;-206.99999999999997;-413.99999999999994;-620.9999999999999;-827.9999999999999;-1034.9999999999998;-1241.9999999999998;-1448.9999999999998;-1655.9999999999998;-1862.9999999999998;-2069.9999999999995;-2276.9999999999995;-2483.9999999999995;-2690.9999999999995;-2897.9999999999995;")
        }
        // 定义天气动效透明度蒙版
        let opacityDefs = chartContainer.append('defs').attr('id', 'opacityDefs');
        let gradAnimate = opacityDefs.append('linearGradient')
            .attr('id', 'gradAnimate')
            .attr('x1', '0%')
            .attr('y1', '0%')
            .attr('x2', '0%')
            .attr('y2', '100%');
        gradAnimate.append('stop')
            .attr('offset', '0%')
            .attr('stop-color', '#ffffff')
            .attr('stop-opacity', '0')
        gradAnimate.append('stop')
            .attr('offset', '100%')
            .attr('stop-color', '#ffffff')
            .attr('stop-opacity', '0.08')

        // 天气图标辅助线
        dashLineG.append("line")
            .attr('x1', 0)
            .attr('y1', svgHeight - margin.bottom - 40)
            .attr('x2', svgWidth)
            .attr('y2', svgHeight - margin.bottom - 40)
            .attr("stroke", "rgba(255, 255, 255, 0.4)") // 直线的颜色
            .attr("stroke-dasharray", "1 3")

        // 天气图标组
        chartContainer.append("g")
            .attr("class", "weather-icon")
            .selectAll(".weather-icon")
            .data(data)
            .enter()
            .each(function (d) {
                if (d.showIcon) {
                    d3.select(this)
                        .append("image")
                        .attr('xlink:href', getIcon(d.cond_code))
                        .attr('width', "35")
                        .attr("x", x(d.timeValue))
                        .attr("y", svgHeight)
                        .attr("transform", `translate(${margin.left}, -${margin.bottom + 58})`)
                }
                if (findIdByCode(d.cond_code)) {
                    d3.select(this)
                        .append('rect')
                        .attr('x', x(d.timeValue))
                        .attr('y', margin.top)
                        .attr('width', distance)
                        .attr('height', svgHeight - margin.bottom - margin.top)
                        .attr('fill', `url(#${findIdByCode(d.cond_code)})`);

                    d3.select(this)
                        .append('rect')
                        .attr('x', x(d.timeValue))
                        .attr('y', margin.top)
                        .attr('width', distance)
                        .attr('height', svgHeight - margin.bottom - margin.top)
                        .attr('fill', 'url(#gradAnimate)')
                }
            })

        // 添加文字元素-湿度(图片以及湿度值)
        chartContainer.append("g")
            .attr("class", "hum-text")
            .selectAll(".hum-text")
            .data(data)
            .enter()
            .each(function (d) {
                d3.select(this)
                    .append("image")
                    .attr('xlink:href', water)
                    .attr("x", function (d, i) {
                        return x(d.timeValue);
                    })
                    .attr("y", svgHeight)
                    .attr("transform", `translate(${margin.left}, -${margin.bottom + 19})`)

                d3.select(this)
                    .append("text")
                    .attr("x", function (d, i) {
                        return x(d.timeValue);
                    })
                    .attr("y", svgHeight)
                    .text(function (d) {
                        return d.precip * 100 + "%";
                    })
                    .attr('fill', '#fff')
                    .attr('style', 'font-size: 12px;')
                    .attr("transform", `translate(${margin.left + 8}, -${margin.bottom + 10})`)
            })

        // 添加文字元素-温度
        chartContainer.append("g")
            .attr("class", "temp-text")
            .selectAll(".temp-text")
            .data(data)
            .enter()
            .append("text")
            .attr("transform", "translate(5, -10)")
            // .attr("transform","translate(" + xScale.bandwidth()/2 + "," + padding.top + ")")
            .attr("x", function (d, i) {
                return x(d.timeValue);
            })
            .attr("y", function (d) {
                return y(d.tmp);
            })
            .text(function (d) {
                return d.tmp + "°";
            })
            .attr('fill', '#fff')
            .attr('style', 'font-size: 14px;')

        // 平滑折线图
        const line = d3.line()
            .x(d => x(d.timeValue))
            .y(d => y(d.tmp))
            .curve(d3.curveCatmullRom.alpha(0.5));

        const createTempGradient = select => {
            const gradient = select
                .select('#maskDefs')
                .append('linearGradient')
                .attr('id', 'tempGradient')
                .attr('x1', '0%')
                .attr('y1', '0%')
                .attr('x2', '100%')
                .attr('y2', '0%');

            const step = 100 / data.length;
            const colors = [
                'rgba(131, 239, 212, 1)',
                'rgba(189, 255, 167, 1)',
                'rgba(255, 219, 140, 1)',
                'rgba(255, 123, 119, 1)',
            ]; // 从温度从低到高颜色排序
            data.forEach((item, index) => {
                let level = 0;
                if (item.tmp < 10) {
                    level = 0
                } else if (item.tmp >= 10 && item.tmp < 18) {
                    level = 1
                } else if (item.tmp >= 18 && item.tmp < 27) {
                    level = 2
                } else if (item.tmp >= 27) {
                    level = 3
                }
                return gradient
                    .append('stop')
                    .attr('offset', `${step * index}%`)
                    .attr('stop-color', colors[level])
            })
        }

        const createOpacityGradient = select => {
            const gradient = select
                .select('#maskDefs')
                .append('linearGradient')
                .attr('id', 'opacityGradient')
                .attr('x1', '0%')
                .attr('y1', '0%')
                .attr('x2', '0%')
                .attr('y2', '100%');
            const step = 100 / data.length;
            data.forEach((item, index) => {
                return gradient
                    .append('stop')
                    .attr('offset', `${step * index}%`)
                    .attr('stop-color', '#c4c4c4')
                    .attr('stop-opacity', `${1 - step * index / 100}`)
            })
        }

        let maskDefs = chartContainer.append('defs').attr('id', 'maskDefs');
        chartContainer.call(createTempGradient);
        chartContainer.call(createOpacityGradient);

        // 创建一个 <mask> 元素
        const mask = maskDefs.append('mask').attr('id', 'opacityMask');

        // 向 <mask> 元素中添加一个 <rect> 元素作为蒙版
        mask.append('rect')
            .attr('x', 0)
            .attr('y', 0)
            .attr('width', svgWidth) // 假设 width 为线条的宽度
            .attr('height', svgHeight - margin.bottom) // 假设 height 为线条的高度
            .attr('fill', 'url(#opacityGradient)'); // 可以使用线条的填充颜色作为蒙版的颜色

        chartContainer.append("path")
            .datum(data)
            .attr("class", "line")
            .attr("d", line)
            .attr('stroke-width', '1')
            .style('fill', 'none')
            .attr('stroke', '#eee')

        let lineD = ""
        chartContainer.select('.line')
            .style('d', d => {
                lineD = line(d).slice(1)
            })

        chartContainer.append("path")
            .attr("class", "line-gradient")
            .attr("d", d => {
                const splitedValues = lineD.split(',');
                return `M0,${svgHeight},${lineD},l0,${svgHeight - splitedValues[splitedValues.length - 1]}`
            })
            .style('fill', 'url(#tempGradient)')
            .style('mask', 'url(#opacityMask)') // 将蒙版应用到线条上
    }, [])

    return (
        <div className="weather-chart-show">
            <svg className="chart-show"></svg>
        </div>
    )
}

export default WeatherChart